To draw insights from the data using visualization
techniques!
Dataset Attributes :
show_id : Unique ID for every Movie / Tv Show
type : Identifier - A Movie or TV Show
title : Title of the Movie / Tv Show
director : Director of the Movie
cast : Actors involved in the movie / show
country : Country where the movie / show was
produced
date_added : Date it was added on Netflix
release_year : Actual Release year of the move /
show
rating : TV Rating of the movie / show
duration : Total Duration - in minutes or number of
seasons
listed_in : Genre
description : The summary description
Loading Library and Data Reading
Warning: package ‘tidyverse’ was built under R version 4.2.2
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
── Attaching packages ──────────────────────────────────────────────────── tidyverse 1.3.2 ──
✔ tibble 3.1.8 ✔ purrr 0.3.5
✔ readr 2.1.3 ✔ forcats 0.5.2
Warning: package ‘tibble’ was built under R version 4.2.2
Warning: package ‘readr’ was built under R version 4.2.2
Warning: package ‘purrr’ was built under R version 4.2.2
Warning: package ‘forcats’ was built under R version 4.2.2
── Conflicts ─────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ data.table::between() masks dplyr::between()
✖ dplyr::filter() masks stats::filter()
✖ data.table::first() masks dplyr::first()
✖ dplyr::lag() masks stats::lag()
✖ data.table::last() masks dplyr::last()
✖ purrr::transpose() masks data.table::transpose()
library(dplyr)
library(ggplot2)
library(readr)
library(tibble)
library(plotly)
Warning: package ‘plotly’ was built under R version 4.2.2
Attaching package: ‘plotly’
The following object is masked from ‘package:ggplot2’:
last_plot
The following object is masked from ‘package:stats’:
filter
The following object is masked from ‘package:graphics’:
layout
Netflix <- read_csv("netflix_titles.csv")
Rows: 8807 Columns: 12
── Column specification ─────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (11): show_id, type, title, director, cast, country, date_added, rating, duration, li...
dbl (1): release_year
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
In the data set there are 8,807 observation of 12 variables describe
the Tv shows,cast,director,release year, rating and many more.
Data cleaning
As a first step we can remove uninformative variables from the
dataset. In our case it is a show_id varaible. The description variable
will not be used for the exploratory data analysis, but can be used to
find similar movies and tv shows using the text similarities.
#drop show_id column
Netflix = subset(Netflix, select = -c(show_id) )
Descriptive Summary
Warning: package ‘modelsummary’ was built under R version 4.2.2
datasummary((` Type` = type) ~ N + Percent(), data = Netflix, title = "Netflix Contnet Type")
Netflix Contnet Type
| Type |
N |
Percent |
| Movie |
6131 |
69.62 |
| TV Show |
2676 |
30.38 |
# Data summary for rating
datasummary((`Rating` = rating )~ N + Percent(), data = Netflix, title = "Rating Categories")
Rating Categories
| Rating |
N |
Percent |
| 66 min |
1 |
0.01 |
| 74 min |
1 |
0.01 |
| 84 min |
1 |
0.01 |
| G |
41 |
0.47 |
| NC-17 |
3 |
0.03 |
| NR |
80 |
0.91 |
| PG |
287 |
3.26 |
| PG-13 |
490 |
5.56 |
| R |
799 |
9.07 |
| TV-14 |
2160 |
24.53 |
| TV-G |
220 |
2.50 |
| TV-MA |
3207 |
36.41 |
| TV-PG |
863 |
9.80 |
| TV-Y |
307 |
3.49 |
| TV-Y7 |
334 |
3.79 |
| TV-Y7-FV |
6 |
0.07 |
| UR |
3 |
0.03 |
#print number of missing values for each variable
data.frame("variable"=c(colnames(Netflix)), "missing values count"=sapply(Netflix, function(x) sum(is.na(x))), row.names=NULL)
From the above output we see that we have missing values for
variables director, cast, country, data_added,rating and duration. Since
rating is the categorical variable with 14 levels we can fill in
(approximate) the missing values for rating with a mode.
#function to find a mode
getmode <- function(v) {
uniqv <- unique(v)
uniqv[which.max(tabulate(match(v, uniqv)))]
}
Netflix$rating[is.na(Netflix$rating)] <- getmode(Netflix$rating)
We can change the date format of the data_added varible for easier
manipulations further.
Netflix$date_added <- as.Date(Netflix$date_added, format = "%B %d, %Y")
The missing values for the variables director, cast and country,
date_added can not be easily approximated, so for now we are going to
continue without filling them. We are going to drop the missing values,
at point where it will be necessary. We also drop duplicated rows in the
dataset based on the title, country, type, release_year variables
#drop duplicated rows based on the title, country, type and release_year
Netflix=distinct(Netflix,title,country,type,release_year, .keep_all= TRUE)
We have done the data cleaning steps and can continue with exploring
the data.
DATA VISUALISATION
Amount Of Netflix by Content
content_by_type <- Netflix%>% group_by(type) %>%
summarise(count = n())
# In ggplot2 library, the code is created by two parts. First one is ggplot(), here we have to specify our arguments such as data, x and y axis and fill type. then continue with + and type of the graph will be added by using geom_graphytype.
Netflix_fig1 <- ggplot(data = content_by_type, aes(x= type, y= count, fill= type))+
geom_bar(colour ="black", fill = "Blue" , stat = "identity")+
guides(fill= FALSE)+
xlab("Netflix Content by Type") + ylab("Amount of Netflix Content")+
ggtitle("Amount of Netflix Content By Type")
Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as of ggplot2
3.3.4.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.

As we see from above there are more than 2 times more Movies than TV
Shows on Netflix.
Since many movies and tv shows are made by several countries (country
variable), to correctly count the total amount of content produced by
each country we need to split strings in country variable and count the
total occurence of each country on its own.
Amount of Netflix Content by Top 13 Country
# 1: split the countries (ex: "United States, India, South Korea, China" form to 'United States' 'India' 'South Korea' 'China') in the country column by using strsplit() function and then assign this operation to "k" for future use.
s <- strsplit(Netflix$country, split = ", ")
# 2: Created a new data frame by using data.frame() function. First column should be type = second one country=. Created type column by using rep() function. The function replicates the values in netds$type depends on the length of each element of s. we used sapply()) function. Now s is our new data in sapply().
Netflix_countries_fuul <- data.frame(type = rep(Netflix$type, sapply(s, length)), country = unlist(s))
# 3: Changed the elements of country column as character by using as.charachter() function.
Netflix_countries_fuul$country <- as.character(gsub(",","",Netflix_countries_fuul$country))
# 4: we created new grouped data frame by the name of amount_by_country NA.omit() function deletes the NA values on the country column/variable. Then we groupped countries and types by using group_by() function (in the "dplyr" library).
amount_by_country <- na.omit(Netflix_countries_fuul) %>%
group_by(country, type) %>%
summarise(count = n())
`summarise()` has grouped output by 'country'. You can override using the `.groups`
argument.
# 5: we can use the "amount_by_country" data frame to observe number of TV Show or Movie in countries. However, this list is too big to be visualized. Thus, we will create a new data frame as table to see just top 10 countries by the name of "w".
w <- reshape(data=data.frame(amount_by_country),idvar="country",
v.names = "count",
timevar = "type",
direction="wide") %>% arrange(desc(count.Movie)) %>%
top_n(13)
Selecting by count.TV Show
# 6: names of the second and third columns are changed by using names() function as seen below.
names(w)[2] <- "number_of_movie"
names(w)[3] <- "number_of_tv_show"
# 7: In the arrange() function we sorted our count.movie columns as descending but, now, we want to change this sort depends on the total values of "number of Movies" and "number of TV Shows". To sort a data frame in R, use the order() function. By default, sorting is ASCENDING. Therefore, we have to specify as descending. + is used to specify total operation.
w <- w[order(desc(w$number_of_movie +w$number_of_tv_show)),]
# 8: Now we can create our graph by using ggplot2 library.
library(ggplot2)
Netflix_Fig2 <- ggplot(w, aes(number_of_movie, number_of_tv_show, colour=country))+
geom_point(size=5)+
xlab("Number of Movies") + ylab("Number of TV Shows")+
ggtitle("Amount of Netflix Content By Top 13 Country")
ggplotly(Netflix_Fig2, dynamicTicks = T)
We can clearly see that United state is a clear on top in the Amount
of content on Netflix. Countries as japan, South Korea, Taiwan having
more TV shoes as compared to Movies.
Amount of Netflix content By Time
#new_date is added to visualise the data more easy
df1 = Netflix %>% group_by(date_added) %>% summarise(added_today = n()) %>%
mutate(total_number_of_content = cumsum(added_today), type = "Total")
df_by_date <- df1 %>% group_by(date_added,type) %>% summarise(added_today = n()) %>% ungroup() %>% group_by(type) %>% mutate(total_number_of_content = cumsum(added_today))
`summarise()` has grouped output by 'date_added'. You can override using the `.groups`
argument.
#Using rbind() function represents a row bind function for vectors, data frames, and matrices to be arranged as rows.
#common = intersect(colnames(df1), colnames(df_by_date))
#full_data<- rbind(df1[common], df_by_date[common])
full_data <- rbind(as.data.frame(df1), as.data.frame(df_by_date))
View(full_data)
Netflix_Fig3 <- plot_ly(full_data, x = ~date_added, y = ~total_number_of_content, color = ~type, type = 'scatter', mode = 'lines', colors=c("#399ba3", "#9addbd", "#bd3939"))
library(ggplot2)
Netflix_Fig3 <- Netflix_Fig3 %>% layout(yaxis = list(title = 'Count'), xaxis = list(title = 'Date'), title="Amout Of Content As A Function Of Time")
Netflix_Fig3
We notice how fast the amount of movies on Netflix overcame the
amount of TV Shows.
Amount of Content by Rating
library(plotly)
df_by_rating_full = Netflix %>% group_by(rating) %>% summarise(count = n())
Netflix_fig4 = plot_ly(df_by_rating_full, labels = ~rating, values = ~count, type = 'pie')
Netflix_fig4 = Netflix_fig4 %>% layout(title = 'Amount of content of Rating', xaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE),
yaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE))
Netflix_fig4
The TV-MA rating is used to create the most content. A television
program that was only intended for mature audiences is given the TV-MA
rating by the TV Parental Guidelines.
The second-largest category is TV-14, which refers to material that
can be unsuitable for minors under the age of 14.
The incredibly popular R rating comes in third place. The Motion
Picture Association of America determines that an R-rated film contains
material that would be inappropriate for children under the age of 17;
the MPAA states that “Under 17 requires accompanying parent or adult
guardian.”
Amount of content Rating (Movie vs Tv shows)
df_by_rating_full = Netflix %>% group_by(rating,type) %>% summarise(count = n())
`summarise()` has grouped output by 'rating'. You can override using the `.groups` argument.
names(df_by_rating_full) [1] <- "rating"
names(df_by_rating_full) [2] <- "type"
names(df_by_rating_full) [3] <- "content"
newdata2 <- reshape(data=data.frame(df_by_rating_full),idvar="rating",
v.names = "content",
timevar = "type",
direction="wide")
names(newdata2)[2] <- "Movie"
names(newdata2)[3] <- "TV Show"
newdata2$`TV Show`[is.na(newdata2$`TV Show`)] <- print(0)
[1] 0
# visualisation
library(plotly)
rating <- newdata2$rating
Movie <- newdata2$Movie
Tv_Show <- newdata2$`TV Show`
Netflix_fig5 = plot_ly(newdata2, x = ~rating, y = ~Movie, type = 'bar', name = 'Movie', marker = list(color = '#bd3939'))
Netflix_fig5 <- Netflix_fig5 %>% add_trace(y = ~Tv_Show, name = 'TV Show', marker = list(color = '#399ba3'))
Netflix_fig5 <- Netflix_fig5 %>% layout(yaxis = list(title = 'Count'),
barmode = 'stack',
title="Amount of Content By Rating (Movie vs. TV Show)")
Netflix_fig5
Which countries are producing most shows
library(ggplot2)
Netflix_fig6 = Netflix %>% group_by(type) %>% mutate(country = fct_infreq(country)) %>% ggplot(aes(x = country)) +
geom_histogram(stat = 'count') + facet_wrap(~type, scales = 'free_x') +
theme_bw() + coord_cartesian(xlim = c(1,10)) + scale_x_discrete(labels = function(x){str_wrap(x,20)}, breaks = function(x) {x[1:10]})
Warning in geom_histogram(stat = "count") :
Ignoring unknown parameters: `binwidth`, `bins`, and `pad`

From the above we can see that :
- After United States, India is the largest source of Movies listed on
Netflix.
- There is no India Tv Shows as much Indian Movies
Top Genres on Netflix
Netflix_generes = strsplit(Netflix$listed_in, split = ", ")
genres_listed_in <- data.frame(type = rep(Netflix$type, sapply(Netflix_generes, length)),
listed_in = unlist(Netflix_generes))
genres_listed_in$listed_in <- as.character(gsub(",","",genres_listed_in$listed_in))
df_list = genres_listed_in %>%
group_by(type, listed_in) %>%
summarise(count = n()) %>%
arrange(desc(count)) %>% top_n(10)
`summarise()` has grouped output by 'type'. You can override using the `.groups` argument.
Selecting by count
Netlfix_fig7 = plot_ly(df_list, x = ~listed_in, y = ~count,
type = 'bar', color = ~type,
colors = c("#bd3939", "#399ba3")) %>%
layout(xaxis = list(categoryorder = "array",
categoryarray = df_list$listed_in,
title = 'Genre',
tickangle = 45),
yaxis = list(title = 'Count'),
title = "Top Genres (Movie vs. TV Show)", margin = list(t = 54),
legend = list(x = 100, y = 0.5))
Netlfix_fig7
We observe that the most popular genre in both movies and TV shows is
international content, which is followed by dramas and comedies. These
are the top three categories on Netflix with the most content!
How are the generes clustered
Warning: package ‘tm’ was built under R version 4.2.2
Loading required package: NLP
Attaching package: ‘NLP’
The following object is masked from ‘package:ggplot2’:
annotate
# building corpus
corpus <- Corpus(VectorSource(Netflix$listed_in))
# create term document matrix
tdm <- TermDocumentMatrix(corpus,
control = list(minWordLength=c(1,Inf)))
# convert to matrix
m <- as.matrix(tdm)
# Hierarchical word clustering using dendrogram
distance <- dist(scale(m))
hc <- hclust(distance, method = "ward.D")
#fviz_dend(hc, cex = 0.5, k = 4, color_labels_by_k = TRUE)
#fviz_dend(hc, cex = 0.7, lwd = 0.5, k = 5,rect = TRUE,rect_fill = TRUE,type = "circular",ylab ="")
#fviz_dend(hc)
# Circular
library(dendextend)
Warning: package ‘dendextend’ was built under R version 4.2.2
---------------------
Welcome to dendextend version 1.16.0
Type citation('dendextend') for how to cite the package.
Type browseVignettes(package = 'dendextend') for the package vignette.
The github page is: https://github.com/talgalili/dendextend/
Suggestions and bug-reports can be submitted at: https://github.com/talgalili/dendextend/issues
You may ask questions at stackoverflow, use the r and dendextend tags:
https://stackoverflow.com/questions/tagged/dendextend
To suppress this message use: suppressPackageStartupMessages(library(dendextend))
---------------------
Attaching package: ‘dendextend’
The following object is masked from ‘package:data.table’:
set
The following object is masked from ‘package:stats’:
cutree
#install.packages("dplyr")
library(dplyr)
require(factoextra)
Loading required package: factoextra
Warning: package ‘factoextra’ was built under R version 4.2.2
Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
Circ = fviz_dend(hc, cex = 0.7, lwd = 0.5, k = 5,
rect = TRUE,
k_colors = c("#440154", "#3b528b", "#21918c", "#5ec962", "#fde725"),
rect_border = c("#440154", "#3b528b", "#21918c", "#5ec962", "#fde725"),
rect_fill = TRUE,
type = "circular",
ylab = "")
Circ

The clusters of genres are depicted in the image below. The clusters
show that family and kid-friendly movies are popular. Moreover, the
largest genre cluster includes several other genres as well as
thrillers, crimes, horror, and reality.
Movie Duration in Top 12 Countries
movie_duration = na.omit(Netflix[Netflix$type == "Movie",][,c("country", "duration")])
Duration<- strsplit(movie_duration$country, split = ", ")
duration_full <- data.frame(duration = rep(movie_duration$duration,
sapply(Duration, length)),
country = unlist(Duration))
duration_full$duration <- as.numeric(gsub(" min","", duration_full$duration))
duration_full_subset <- duration_full[duration_full$country %in%
c("United States", "India", "United Kingdom",
"Canada", "France", "Japan", "Spain", "South Korea",
"Mexico", "Australia", "China", "Taiwan"),]
Netflix_fig8 = plot_ly(duration_full_subset, y = ~duration, color = ~country, type = "box") %>%
layout(xaxis = list(title = "Country"),
yaxis = list(title = 'Duration (in min)'),
title = "Box-Plots of Movie Duration in Top 12 Countries", margin = list(t = 54),
legend = list(x = 100, y = 0.5))
Netflix_fig8
Warning in RColorBrewer::brewer.pal(N, "Set2") :
n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
Warning in RColorBrewer::brewer.pal(N, "Set2") :
n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
Warning in RColorBrewer::brewer.pal(N, "Set2") :
n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
Warning in RColorBrewer::brewer.pal(N, "Set2") :
n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
Conclusion
It is clear that movies play a bigger role in Netflix content based
on the visualization and text analysis of the Netflix data.
Additionally, the data summary and visualization showed that the United
States was the sole country at the time that Netflix began donating its
content, which was in 2008. Additionally, Netflix TV series and movies
are rated, and these ratings are consistent for various audiences. The
examination of runtime and ranting categories reveals that movies aimed
towards youngsters are typically shorter.
LS0tDQp0aXRsZTogIk5ldGZsaXg6IERhdGEgVmlzdWFsaXNhdGlvbiBhbmQgS2V5IEluc2lnaHQiDQphdXRob3I6ICJBbWlzaGEgR2FyZyAmIEJoYXZhbmEgQmFuZGFydSINCmRhdGU6ICIwOS0xMi0yMDIyIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDMNCiAgICB0aGVtZTogc3BhY2VsYWINCiAgICBoaWdobGlnaHQ6IHRhbmdvDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29sbGFwc2VkOiBmYWxzZQ0KLS0tDQoNCg0KKipUbyBkcmF3IGluc2lnaHRzIGZyb20gdGhlIGRhdGEgdXNpbmcgdmlzdWFsaXphdGlvbiB0ZWNobmlxdWVzISoqDQoNCiMjIyAqKkRhdGFzZXQgQXR0cmlidXRlcyA6KioNCg0KKipzaG93X2lkKiogOiBVbmlxdWUgSUQgZm9yIGV2ZXJ5IE1vdmllIC8gVHYgU2hvdw0KDQoqKnR5cGUqKiA6IElkZW50aWZpZXIgLSBBIE1vdmllIG9yIFRWIFNob3cNCg0KKip0aXRsZSoqIDogVGl0bGUgb2YgdGhlIE1vdmllIC8gVHYgU2hvdw0KDQoqKmRpcmVjdG9yKiogOiBEaXJlY3RvciBvZiB0aGUgTW92aWUNCg0KKipjYXN0KiogOiBBY3RvcnMgaW52b2x2ZWQgaW4gdGhlIG1vdmllIC8gc2hvdw0KDQoqKmNvdW50cnkqKiA6IENvdW50cnkgd2hlcmUgdGhlIG1vdmllIC8gc2hvdyB3YXMgcHJvZHVjZWQNCg0KKipkYXRlX2FkZGVkKiogOiBEYXRlIGl0IHdhcyBhZGRlZCBvbiBOZXRmbGl4DQoNCioqcmVsZWFzZV95ZWFyKiogOiBBY3R1YWwgUmVsZWFzZSB5ZWFyIG9mIHRoZSBtb3ZlIC8gc2hvdw0KDQoqKnJhdGluZyoqIDogVFYgUmF0aW5nIG9mIHRoZSBtb3ZpZSAvIHNob3cNCg0KKipkdXJhdGlvbioqIDogVG90YWwgRHVyYXRpb24gLSBpbiBtaW51dGVzIG9yIG51bWJlciBvZiBzZWFzb25zDQoNCioqbGlzdGVkX2luKiogOiBHZW5yZQ0KDQoqKmRlc2NyaXB0aW9uKiogOiBUaGUgc3VtbWFyeSBkZXNjcmlwdGlvbg0KDQojIyMgKipMb2FkaW5nIExpYnJhcnkgYW5kIERhdGEgUmVhZGluZyoqDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KHRpYmJsZSkNCmxpYnJhcnkocGxvdGx5KQ0KTmV0ZmxpeCA8LSByZWFkX2NzdigibmV0ZmxpeF90aXRsZXMuY3N2IikNClZpZXcoTmV0ZmxpeCkNCmBgYA0KYGBge3J9DQpoZWFkKE5ldGZsaXgpDQpgYGANCg0KSW4gdGhlIGRhdGEgc2V0IHRoZXJlIGFyZSA4LDgwNyBvYnNlcnZhdGlvbiBvZiAxMiB2YXJpYWJsZXMgZGVzY3JpYmUgdGhlIFR2IHNob3dzLGNhc3QsZGlyZWN0b3IscmVsZWFzZSB5ZWFyLCByYXRpbmcgYW5kIG1hbnkgbW9yZS4NCg0KIyMjICoqRGF0YSBjbGVhbmluZyoqDQpBcyBhIGZpcnN0IHN0ZXAgd2UgY2FuIHJlbW92ZSB1bmluZm9ybWF0aXZlIHZhcmlhYmxlcyBmcm9tIHRoZSBkYXRhc2V0LiBJbiBvdXIgY2FzZSBpdCBpcyBhIHNob3dfaWQgdmFyYWlibGUuIFRoZSBkZXNjcmlwdGlvbiB2YXJpYWJsZSB3aWxsIG5vdCBiZSB1c2VkIGZvciB0aGUgZXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcywgYnV0IGNhbiBiZSB1c2VkIHRvIGZpbmQgc2ltaWxhciBtb3ZpZXMgYW5kIHR2IHNob3dzIHVzaW5nIHRoZSB0ZXh0IHNpbWlsYXJpdGllcy4NCg0KYGBge3J9DQojZHJvcCBzaG93X2lkIGNvbHVtbg0KTmV0ZmxpeCA9IHN1YnNldChOZXRmbGl4LCBzZWxlY3QgPSAtYyhzaG93X2lkKSApDQpgYGANCg0KDQojIyMgKipEZXNjcmlwdGl2ZSBTdW1tYXJ5KioNCmBgYHtyfQ0KbGlicmFyeShtb2RlbHN1bW1hcnkpDQpkYXRhc3VtbWFyeSgoYCBUeXBlYCA9IHR5cGUpIH4gTiArIFBlcmNlbnQoKSwgZGF0YSA9IE5ldGZsaXgsIHRpdGxlID0gIk5ldGZsaXggQ29udG5ldCBUeXBlIikNCmBgYA0KYGBge3J9DQojIERhdGEgc3VtbWFyeSBmb3IgcmF0aW5nIA0KZGF0YXN1bW1hcnkoKGBSYXRpbmdgID0gcmF0aW5nICl+IE4gKyBQZXJjZW50KCksIGRhdGEgPSBOZXRmbGl4LCB0aXRsZSA9ICJSYXRpbmcgQ2F0ZWdvcmllcyIpDQpgYGANCg0KDQpgYGB7cn0NCiNwcmludCBudW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMgZm9yIGVhY2ggdmFyaWFibGUNCmRhdGEuZnJhbWUoInZhcmlhYmxlIj1jKGNvbG5hbWVzKE5ldGZsaXgpKSwgIm1pc3NpbmcgdmFsdWVzIGNvdW50Ij1zYXBwbHkoTmV0ZmxpeCwgZnVuY3Rpb24oeCkgc3VtKGlzLm5hKHgpKSksIHJvdy5uYW1lcz1OVUxMKQ0KYGBgDQoNCkZyb20gdGhlIGFib3ZlIG91dHB1dCB3ZSBzZWUgdGhhdCB3ZSBoYXZlIG1pc3NpbmcgdmFsdWVzIGZvciB2YXJpYWJsZXMgZGlyZWN0b3IsIGNhc3QsIGNvdW50cnksIGRhdGFfYWRkZWQscmF0aW5nIGFuZCBkdXJhdGlvbi4gU2luY2UgcmF0aW5nIGlzIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSB3aXRoIDE0IGxldmVscyB3ZSBjYW4gZmlsbCBpbiAoYXBwcm94aW1hdGUpIHRoZSBtaXNzaW5nIHZhbHVlcyBmb3IgcmF0aW5nIHdpdGggYSBtb2RlLg0KDQpgYGB7cn0NCiNmdW5jdGlvbiB0byBmaW5kIGEgbW9kZQ0KZ2V0bW9kZSA8LSBmdW5jdGlvbih2KSB7DQogICB1bmlxdiA8LSB1bmlxdWUodikNCiAgIHVuaXF2W3doaWNoLm1heCh0YWJ1bGF0ZShtYXRjaCh2LCB1bmlxdikpKV0NCn0NCk5ldGZsaXgkcmF0aW5nW2lzLm5hKE5ldGZsaXgkcmF0aW5nKV0gPC0gZ2V0bW9kZShOZXRmbGl4JHJhdGluZykNCmBgYA0KDQpXZSBjYW4gY2hhbmdlIHRoZSBkYXRlIGZvcm1hdCBvZiB0aGUgZGF0YV9hZGRlZCB2YXJpYmxlIGZvciBlYXNpZXIgbWFuaXB1bGF0aW9ucyBmdXJ0aGVyLg0KDQpgYGB7cn0NCk5ldGZsaXgkZGF0ZV9hZGRlZCA8LSBhcy5EYXRlKE5ldGZsaXgkZGF0ZV9hZGRlZCwgZm9ybWF0ID0gIiVCICVkLCAlWSIpDQpgYGANCg0KVGhlIG1pc3NpbmcgdmFsdWVzIGZvciB0aGUgdmFyaWFibGVzIGRpcmVjdG9yLCBjYXN0IGFuZCBjb3VudHJ5LCBkYXRlX2FkZGVkIGNhbiBub3QgYmUgZWFzaWx5IGFwcHJveGltYXRlZCwgc28gZm9yIG5vdyB3ZSBhcmUgZ29pbmcgdG8gY29udGludWUgd2l0aG91dCBmaWxsaW5nIHRoZW0uIFdlIGFyZSBnb2luZyB0byBkcm9wIHRoZSBtaXNzaW5nIHZhbHVlcywgYXQgcG9pbnQgd2hlcmUgaXQgd2lsbCBiZSBuZWNlc3NhcnkuIFdlIGFsc28gZHJvcCBkdXBsaWNhdGVkIHJvd3MgaW4gdGhlIGRhdGFzZXQgYmFzZWQgb24gdGhlIHRpdGxlLCBjb3VudHJ5LCB0eXBlLCByZWxlYXNlX3llYXIgdmFyaWFibGVzDQoNCmBgYHtyfQ0KI2Ryb3AgZHVwbGljYXRlZCByb3dzIGJhc2VkIG9uIHRoZSB0aXRsZSwgY291bnRyeSwgdHlwZSBhbmQgcmVsZWFzZV95ZWFyDQpOZXRmbGl4PWRpc3RpbmN0KE5ldGZsaXgsdGl0bGUsY291bnRyeSx0eXBlLHJlbGVhc2VfeWVhciwgLmtlZXBfYWxsPSBUUlVFKQ0KYGBgDQoNCldlIGhhdmUgZG9uZSB0aGUgZGF0YSBjbGVhbmluZyBzdGVwcyBhbmQgY2FuIGNvbnRpbnVlIHdpdGggZXhwbG9yaW5nIHRoZSBkYXRhLg0KDQojIyMgKipEQVRBIFZJU1VBTElTQVRJT04qKg0KDQojIyMgKipBbW91bnQgT2YgTmV0ZmxpeCBieSBDb250ZW50KioNCg0KYGBge3J9DQpjb250ZW50X2J5X3R5cGUgPC0gTmV0ZmxpeCU+JSBncm91cF9ieSh0eXBlKSAlPiUgDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkNCiMgSW4gZ2dwbG90MiBsaWJyYXJ5LCB0aGUgY29kZSBpcyBjcmVhdGVkIGJ5IHR3byBwYXJ0cy4gRmlyc3Qgb25lIGlzIGdncGxvdCgpLCBoZXJlIHdlIGhhdmUgdG8gc3BlY2lmeSBvdXIgYXJndW1lbnRzIHN1Y2ggYXMgZGF0YSwgeCBhbmQgeSBheGlzIGFuZCBmaWxsIHR5cGUuIHRoZW4gY29udGludWUgd2l0aCArIGFuZCB0eXBlIG9mIHRoZSBncmFwaCB3aWxsIGJlIGFkZGVkIGJ5IHVzaW5nIGdlb21fZ3JhcGh5dHlwZS4NCk5ldGZsaXhfZmlnMSAgPC0gZ2dwbG90KGRhdGEgPSBjb250ZW50X2J5X3R5cGUsIGFlcyh4PSB0eXBlLCB5PSBjb3VudCwgZmlsbD0gdHlwZSkpKw0KICBnZW9tX2Jhcihjb2xvdXIgPSJibGFjayIsIGZpbGwgPSAiQmx1ZSIgLCAgc3RhdCA9ICJpZGVudGl0eSIpKw0KICBndWlkZXMoZmlsbD0gRkFMU0UpKw0KICB4bGFiKCJOZXRmbGl4IENvbnRlbnQgYnkgVHlwZSIpICsgeWxhYigiQW1vdW50IG9mIE5ldGZsaXggQ29udGVudCIpKw0KICBnZ3RpdGxlKCJBbW91bnQgb2YgTmV0ZmxpeCBDb250ZW50IEJ5IFR5cGUiKQ0KTmV0ZmxpeF9maWcxDQpgYGANCg0KQXMgd2Ugc2VlIGZyb20gYWJvdmUgdGhlcmUgYXJlIG1vcmUgdGhhbiAyIHRpbWVzIG1vcmUgTW92aWVzIHRoYW4gVFYgU2hvd3Mgb24gTmV0ZmxpeC4NCg0KU2luY2UgbWFueSBtb3ZpZXMgYW5kIHR2IHNob3dzIGFyZSBtYWRlIGJ5IHNldmVyYWwgY291bnRyaWVzIChjb3VudHJ5IHZhcmlhYmxlKSwgdG8gY29ycmVjdGx5IGNvdW50IHRoZSB0b3RhbCBhbW91bnQgb2YgY29udGVudCBwcm9kdWNlZCBieSBlYWNoIGNvdW50cnkgd2UgbmVlZCB0byBzcGxpdCBzdHJpbmdzIGluIGNvdW50cnkgdmFyaWFibGUgYW5kIGNvdW50IHRoZSB0b3RhbCBvY2N1cmVuY2Ugb2YgZWFjaCBjb3VudHJ5IG9uIGl0cyBvd24uDQoNCiMjIyAqKkFtb3VudCBvZiBOZXRmbGl4IENvbnRlbnQgYnkgVG9wIDEzIENvdW50cnkqKg0KDQpgYGB7cn0NCiMgMTogc3BsaXQgdGhlIGNvdW50cmllcyAoZXg6ICJVbml0ZWQgU3RhdGVzLCBJbmRpYSwgU291dGggS29yZWEsIENoaW5hIiBmb3JtIHRvICdVbml0ZWQgU3RhdGVzJyAnSW5kaWEnICdTb3V0aCBLb3JlYScgJ0NoaW5hJykgaW4gdGhlIGNvdW50cnkgY29sdW1uIGJ5IHVzaW5nIHN0cnNwbGl0KCkgZnVuY3Rpb24gYW5kIHRoZW4gYXNzaWduIHRoaXMgb3BlcmF0aW9uIHRvICJrIiBmb3IgZnV0dXJlIHVzZS4NCnMgPC0gc3Ryc3BsaXQoTmV0ZmxpeCRjb3VudHJ5LCBzcGxpdCA9ICIsICIpDQoNCiMgMjogQ3JlYXRlZCBhIG5ldyBkYXRhIGZyYW1lIGJ5IHVzaW5nIGRhdGEuZnJhbWUoKSBmdW5jdGlvbi4gRmlyc3QgY29sdW1uIHNob3VsZCBiZSB0eXBlID0gc2Vjb25kIG9uZSBjb3VudHJ5PS4gQ3JlYXRlZCB0eXBlIGNvbHVtbiBieSB1c2luZyByZXAoKSBmdW5jdGlvbi4gVGhlIGZ1bmN0aW9uIHJlcGxpY2F0ZXMgdGhlIHZhbHVlcyBpbiBuZXRkcyR0eXBlIGRlcGVuZHMgb24gdGhlIGxlbmd0aCBvZiBlYWNoIGVsZW1lbnQgb2Ygcy4gd2UgdXNlZCBzYXBwbHkoKSkgZnVuY3Rpb24uIE5vdyBzIGlzIG91ciBuZXcgZGF0YSBpbiBzYXBwbHkoKS4NCg0KTmV0ZmxpeF9jb3VudHJpZXNfZnV1bCA8LSBkYXRhLmZyYW1lKHR5cGUgPSByZXAoTmV0ZmxpeCR0eXBlLCBzYXBwbHkocywgbGVuZ3RoKSksIGNvdW50cnkgPSB1bmxpc3QocykpDQojIDM6IENoYW5nZWQgdGhlIGVsZW1lbnRzIG9mIGNvdW50cnkgY29sdW1uIGFzIGNoYXJhY3RlciBieSB1c2luZyBhcy5jaGFyYWNodGVyKCkgZnVuY3Rpb24uDQoNCk5ldGZsaXhfY291bnRyaWVzX2Z1dWwkY291bnRyeSA8LSBhcy5jaGFyYWN0ZXIoZ3N1YigiLCIsIiIsTmV0ZmxpeF9jb3VudHJpZXNfZnV1bCRjb3VudHJ5KSkNCg0KIyA0OiB3ZSBjcmVhdGVkIG5ldyBncm91cGVkIGRhdGEgZnJhbWUgYnkgdGhlIG5hbWUgb2YgYW1vdW50X2J5X2NvdW50cnkgTkEub21pdCgpIGZ1bmN0aW9uIGRlbGV0ZXMgdGhlIE5BIHZhbHVlcyBvbiB0aGUgY291bnRyeSBjb2x1bW4vdmFyaWFibGUuIFRoZW4gd2UgZ3JvdXBwZWQgY291bnRyaWVzIGFuZCB0eXBlcyBieSB1c2luZyBncm91cF9ieSgpIGZ1bmN0aW9uIChpbiB0aGUgImRwbHlyIiBsaWJyYXJ5KS4NCg0KYW1vdW50X2J5X2NvdW50cnkgPC0gbmEub21pdChOZXRmbGl4X2NvdW50cmllc19mdXVsKSAlPiUNCiAgZ3JvdXBfYnkoY291bnRyeSwgdHlwZSkgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkNCg0KIyA1OiB3ZSBjYW4gdXNlIHRoZSAiYW1vdW50X2J5X2NvdW50cnkiIGRhdGEgZnJhbWUgdG8gb2JzZXJ2ZSBudW1iZXIgb2YgVFYgU2hvdyBvciBNb3ZpZSBpbiBjb3VudHJpZXMuIEhvd2V2ZXIsIHRoaXMgbGlzdCBpcyB0b28gYmlnIHRvIGJlIHZpc3VhbGl6ZWQuIFRodXMsIHdlIHdpbGwgY3JlYXRlIGEgbmV3IGRhdGEgZnJhbWUgYXMgdGFibGUgdG8gc2VlIGp1c3QgdG9wIDEwIGNvdW50cmllcyBieSB0aGUgbmFtZSBvZiAidyIuDQoNCncgPC0gcmVzaGFwZShkYXRhPWRhdGEuZnJhbWUoYW1vdW50X2J5X2NvdW50cnkpLGlkdmFyPSJjb3VudHJ5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgdi5uYW1lcyA9ICJjb3VudCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHRpbWV2YXIgPSAidHlwZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbj0id2lkZSIpICU+JSBhcnJhbmdlKGRlc2MoY291bnQuTW92aWUpKSAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wX24oMTMpDQoNCiMgNjogbmFtZXMgb2YgdGhlIHNlY29uZCBhbmQgdGhpcmQgY29sdW1ucyBhcmUgY2hhbmdlZCBieSB1c2luZyBuYW1lcygpIGZ1bmN0aW9uIGFzIHNlZW4gYmVsb3cuDQoNCm5hbWVzKHcpWzJdIDwtICJudW1iZXJfb2ZfbW92aWUiDQpuYW1lcyh3KVszXSA8LSAibnVtYmVyX29mX3R2X3Nob3ciDQoNCiMgNzogSW4gdGhlIGFycmFuZ2UoKSBmdW5jdGlvbiB3ZSBzb3J0ZWQgb3VyIGNvdW50Lm1vdmllIGNvbHVtbnMgYXMgZGVzY2VuZGluZyBidXQsIG5vdywgd2Ugd2FudCB0byBjaGFuZ2UgdGhpcyBzb3J0IGRlcGVuZHMgb24gdGhlIHRvdGFsIHZhbHVlcyBvZiAibnVtYmVyIG9mIE1vdmllcyIgYW5kICJudW1iZXIgb2YgVFYgU2hvd3MiLiBUbyBzb3J0IGEgZGF0YSBmcmFtZSBpbiBSLCB1c2UgdGhlIG9yZGVyKCkgZnVuY3Rpb24uIEJ5IGRlZmF1bHQsIHNvcnRpbmcgaXMgQVNDRU5ESU5HLiBUaGVyZWZvcmUsIHdlIGhhdmUgdG8gc3BlY2lmeSBhcyBkZXNjZW5kaW5nLiArIGlzIHVzZWQgdG8gc3BlY2lmeSB0b3RhbCBvcGVyYXRpb24uDQoNCncgPC0gd1tvcmRlcihkZXNjKHckbnVtYmVyX29mX21vdmllICt3JG51bWJlcl9vZl90dl9zaG93KSksXQ0KDQojIDg6IE5vdyB3ZSBjYW4gY3JlYXRlIG91ciBncmFwaCBieSB1c2luZyBnZ3Bsb3QyIGxpYnJhcnkuDQoNCmxpYnJhcnkoZ2dwbG90MikNCk5ldGZsaXhfRmlnMiA8LSBnZ3Bsb3QodywgYWVzKG51bWJlcl9vZl9tb3ZpZSwgbnVtYmVyX29mX3R2X3Nob3csIGNvbG91cj1jb3VudHJ5KSkrIA0KICBnZW9tX3BvaW50KHNpemU9NSkrDQogIHhsYWIoIk51bWJlciBvZiBNb3ZpZXMiKSArIHlsYWIoIk51bWJlciBvZiBUViBTaG93cyIpKw0KICBnZ3RpdGxlKCJBbW91bnQgb2YgTmV0ZmxpeCBDb250ZW50IEJ5IFRvcCAxMyBDb3VudHJ5IikNCmdncGxvdGx5KE5ldGZsaXhfRmlnMiwgZHluYW1pY1RpY2tzID0gVCkNCg0KYGBgDQpXZSBjYW4gY2xlYXJseSBzZWUgdGhhdCBVbml0ZWQgc3RhdGUgaXMgYSBjbGVhciBvbiB0b3AgaW4gdGhlIEFtb3VudCBvZiBjb250ZW50IG9uIE5ldGZsaXguIENvdW50cmllcyBhcyBqYXBhbiwgU291dGggS29yZWEsIFRhaXdhbiBoYXZpbmcgbW9yZSBUViBzaG9lcyBhcyBjb21wYXJlZCB0byBNb3ZpZXMuDQoNCiMjIyAqKkFtb3VudCBvZiBOZXRmbGl4IGNvbnRlbnQgQnkgVGltZSoqDQoNCmBgYHtyfQ0KI25ld19kYXRlIGlzIGFkZGVkIHRvIHZpc3VhbGlzZSB0aGUgZGF0YSBtb3JlIGVhc3kgDQoNCmRmMSA9IE5ldGZsaXggJT4lIGdyb3VwX2J5KGRhdGVfYWRkZWQpICU+JSBzdW1tYXJpc2UoYWRkZWRfdG9kYXkgPSBuKCkpICU+JSANCiAgbXV0YXRlKHRvdGFsX251bWJlcl9vZl9jb250ZW50ID0gY3Vtc3VtKGFkZGVkX3RvZGF5KSwgdHlwZSA9ICJUb3RhbCIpDQoNCmRmX2J5X2RhdGUgPC0gZGYxICU+JSBncm91cF9ieShkYXRlX2FkZGVkLHR5cGUpICU+JSBzdW1tYXJpc2UoYWRkZWRfdG9kYXkgPSBuKCkpICU+JSB1bmdyb3VwKCkgJT4lIGdyb3VwX2J5KHR5cGUpICU+JSBtdXRhdGUodG90YWxfbnVtYmVyX29mX2NvbnRlbnQgPSBjdW1zdW0oYWRkZWRfdG9kYXkpKQ0KDQojVXNpbmcgcmJpbmQoKSBmdW5jdGlvbiByZXByZXNlbnRzIGEgcm93IGJpbmQgZnVuY3Rpb24gZm9yIHZlY3RvcnMsIGRhdGEgZnJhbWVzLCBhbmQgbWF0cmljZXMgdG8gYmUgYXJyYW5nZWQgYXMgcm93cy4NCiNjb21tb24gPSBpbnRlcnNlY3QoY29sbmFtZXMoZGYxKSwgY29sbmFtZXMoZGZfYnlfZGF0ZSkpDQojZnVsbF9kYXRhPC0gcmJpbmQoZGYxW2NvbW1vbl0sIGRmX2J5X2RhdGVbY29tbW9uXSkNCmZ1bGxfZGF0YSA8LSByYmluZChhcy5kYXRhLmZyYW1lKGRmMSksIGFzLmRhdGEuZnJhbWUoZGZfYnlfZGF0ZSkpDQpWaWV3KGZ1bGxfZGF0YSkNCg0KTmV0ZmxpeF9GaWczIDwtIHBsb3RfbHkoZnVsbF9kYXRhLCB4ID0gfmRhdGVfYWRkZWQsIHkgPSB+dG90YWxfbnVtYmVyX29mX2NvbnRlbnQsIGNvbG9yID0gfnR5cGUsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMnLCBjb2xvcnM9YygiIzM5OWJhMyIsICAiIzlhZGRiZCIsICIjYmQzOTM5IikpDQpsaWJyYXJ5KGdncGxvdDIpDQoNCk5ldGZsaXhfRmlnMyA8LSBOZXRmbGl4X0ZpZzMgJT4lIGxheW91dCh5YXhpcyA9IGxpc3QodGl0bGUgPSAnQ291bnQnKSwgeGF4aXMgPSBsaXN0KHRpdGxlID0gJ0RhdGUnKSwgdGl0bGU9IkFtb3V0IE9mIENvbnRlbnQgQXMgQSBGdW5jdGlvbiBPZiBUaW1lIikNCk5ldGZsaXhfRmlnMw0KYGBgDQpXZSAgbm90aWNlIGhvdyBmYXN0IHRoZSBhbW91bnQgb2YgbW92aWVzIG9uIE5ldGZsaXggb3ZlcmNhbWUgdGhlIGFtb3VudCBvZiBUViBTaG93cy4NCg0KIyMjICoqQW1vdW50IG9mIENvbnRlbnQgYnkgUmF0aW5nKioNCmBgYHtyfQ0KbGlicmFyeShwbG90bHkpDQpkZl9ieV9yYXRpbmdfZnVsbCA9IE5ldGZsaXggJT4lIGdyb3VwX2J5KHJhdGluZykgJT4lIHN1bW1hcmlzZShjb3VudCA9IG4oKSkNCk5ldGZsaXhfZmlnNCA9IHBsb3RfbHkoZGZfYnlfcmF0aW5nX2Z1bGwsIGxhYmVscyA9IH5yYXRpbmcsIHZhbHVlcyA9IH5jb3VudCwgdHlwZSA9ICdwaWUnKQ0KTmV0ZmxpeF9maWc0ID0gTmV0ZmxpeF9maWc0ICU+JSBsYXlvdXQodGl0bGUgPSAnQW1vdW50IG9mIGNvbnRlbnQgb2YgUmF0aW5nJywgeGF4aXMgPSBsaXN0KHNob3dncmlkID0gRkFMU0UsIHplcm9saW5lID0gRkFMU0UsIHNob3d0aWNrbGFiZWxzID0gRkFMU0UpLA0KICAgICAgICAgeWF4aXMgPSBsaXN0KHNob3dncmlkID0gRkFMU0UsIHplcm9saW5lID0gRkFMU0UsIHNob3d0aWNrbGFiZWxzID0gRkFMU0UpKQ0KTmV0ZmxpeF9maWc0DQpgYGANClRoZSBUVi1NQSByYXRpbmcgaXMgdXNlZCB0byBjcmVhdGUgdGhlIG1vc3QgY29udGVudC4gQSB0ZWxldmlzaW9uIHByb2dyYW0gdGhhdCB3YXMgb25seSBpbnRlbmRlZCBmb3IgbWF0dXJlIGF1ZGllbmNlcyBpcyBnaXZlbiB0aGUgVFYtTUEgcmF0aW5nIGJ5IHRoZSBUViBQYXJlbnRhbCBHdWlkZWxpbmVzLg0KDQpUaGUgc2Vjb25kLWxhcmdlc3QgY2F0ZWdvcnkgaXMgVFYtMTQsIHdoaWNoIHJlZmVycyB0byBtYXRlcmlhbCB0aGF0IGNhbiBiZSB1bnN1aXRhYmxlIGZvciBtaW5vcnMgdW5kZXIgdGhlIGFnZSBvZiAxNC4NCg0KVGhlIGluY3JlZGlibHkgcG9wdWxhciBSIHJhdGluZyBjb21lcyBpbiB0aGlyZCBwbGFjZS4gVGhlIE1vdGlvbiBQaWN0dXJlIEFzc29jaWF0aW9uIG9mIEFtZXJpY2EgZGV0ZXJtaW5lcyB0aGF0IGFuIFItcmF0ZWQgZmlsbSBjb250YWlucyBtYXRlcmlhbCB0aGF0IHdvdWxkIGJlIGluYXBwcm9wcmlhdGUgZm9yIGNoaWxkcmVuIHVuZGVyIHRoZSBhZ2Ugb2YgMTc7IHRoZSBNUEFBIHN0YXRlcyB0aGF0ICJVbmRlciAxNyByZXF1aXJlcyBhY2NvbXBhbnlpbmcgcGFyZW50IG9yIGFkdWx0IGd1YXJkaWFuLiINCg0KIyMjICoqQW1vdW50IG9mIGNvbnRlbnQgUmF0aW5nIChNb3ZpZSB2cyBUdiBzaG93cykqKg0KDQpgYGB7cn0NCmRmX2J5X3JhdGluZ19mdWxsID0gTmV0ZmxpeCAlPiUgZ3JvdXBfYnkocmF0aW5nLHR5cGUpICU+JSBzdW1tYXJpc2UoY291bnQgPSBuKCkpDQpuYW1lcyhkZl9ieV9yYXRpbmdfZnVsbCkgWzFdIDwtICJyYXRpbmciDQpuYW1lcyhkZl9ieV9yYXRpbmdfZnVsbCkgWzJdIDwtICJ0eXBlIg0KbmFtZXMoZGZfYnlfcmF0aW5nX2Z1bGwpIFszXSA8LSAiY29udGVudCINCm5ld2RhdGEyIDwtIHJlc2hhcGUoZGF0YT1kYXRhLmZyYW1lKGRmX2J5X3JhdGluZ19mdWxsKSxpZHZhcj0icmF0aW5nIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgdi5uYW1lcyA9ICJjb250ZW50IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZXZhciA9ICJ0eXBlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uPSJ3aWRlIikNCm5hbWVzKG5ld2RhdGEyKVsyXSA8LSAiTW92aWUiDQpuYW1lcyhuZXdkYXRhMilbM10gPC0gIlRWIFNob3ciDQpuZXdkYXRhMiRgVFYgU2hvd2BbaXMubmEobmV3ZGF0YTIkYFRWIFNob3dgKV0gPC0gcHJpbnQoMCkNCiMgdmlzdWFsaXNhdGlvbg0KbGlicmFyeShwbG90bHkpDQpyYXRpbmcgPC0gbmV3ZGF0YTIkcmF0aW5nDQpNb3ZpZSA8LSBuZXdkYXRhMiRNb3ZpZQ0KVHZfU2hvdyA8LSBuZXdkYXRhMiRgVFYgU2hvd2ANCk5ldGZsaXhfZmlnNSA9IHBsb3RfbHkobmV3ZGF0YTIsIHggPSB+cmF0aW5nLCB5ID0gfk1vdmllLCB0eXBlID0gJ2JhcicsIG5hbWUgPSAnTW92aWUnLCBtYXJrZXIgPSBsaXN0KGNvbG9yID0gJyNiZDM5MzknKSkNCk5ldGZsaXhfZmlnNSA8LSBOZXRmbGl4X2ZpZzUgJT4lIGFkZF90cmFjZSh5ID0gflR2X1Nob3csIG5hbWUgPSAnVFYgU2hvdycsIG1hcmtlciA9IGxpc3QoY29sb3IgPSAnIzM5OWJhMycpKQ0KTmV0ZmxpeF9maWc1IDwtIE5ldGZsaXhfZmlnNSAlPiUgbGF5b3V0KHlheGlzID0gbGlzdCh0aXRsZSA9ICdDb3VudCcpLA0KICAgICAgICAgICAgICAgICAgICAgICAgYmFybW9kZSA9ICdzdGFjaycsIA0KICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGU9IkFtb3VudCBvZiBDb250ZW50IEJ5IFJhdGluZyAoTW92aWUgdnMuIFRWIFNob3cpIikNCk5ldGZsaXhfZmlnNQ0KYGBgDQoNCg0KIyMjICoqV2hpY2ggY291bnRyaWVzIGFyZSBwcm9kdWNpbmcgbW9zdCBzaG93cyoqDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KTmV0ZmxpeF9maWc2ID0gTmV0ZmxpeCAgJT4lIGdyb3VwX2J5KHR5cGUpICAlPiUgbXV0YXRlKGNvdW50cnkgPSBmY3RfaW5mcmVxKGNvdW50cnkpKSAlPiUgZ2dwbG90KGFlcyh4ID0gY291bnRyeSkpICsgDQogICAgICAgICAgICBnZW9tX2hpc3RvZ3JhbShzdGF0ID0gJ2NvdW50JykgKyBmYWNldF93cmFwKH50eXBlLCBzY2FsZXMgPSAnZnJlZV94JykgKyANCiAgICAgICAgICAgIHRoZW1lX2J3KCkgKyBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMSwxMCkpICsgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KXtzdHJfd3JhcCh4LDIwKX0sIGJyZWFrcyA9IGZ1bmN0aW9uKHgpIHt4WzE6MTBdfSkNCk5ldGZsaXhfZmlnNg0KDQpgYGANCkZyb20gdGhlIGFib3ZlIHdlIGNhbiBzZWUgdGhhdCA6DQoNCjEpIEFmdGVyIFVuaXRlZCBTdGF0ZXMsIEluZGlhIGlzIHRoZSBsYXJnZXN0IHNvdXJjZSBvZiBNb3ZpZXMgbGlzdGVkIG9uIE5ldGZsaXguDQoyKSBUaGVyZSBpcyBubyBJbmRpYSBUdiBTaG93cyBhcyBtdWNoIEluZGlhbiBNb3ZpZXMgDQoNCg0KDQoNCiMjIyAqKlRvcCBHZW5yZXMgb24gTmV0ZmxpeCoqDQpgYGB7cn0NCk5ldGZsaXhfZ2VuZXJlcyA9IHN0cnNwbGl0KE5ldGZsaXgkbGlzdGVkX2luLCBzcGxpdCA9ICIsICIpDQpnZW5yZXNfbGlzdGVkX2luIDwtIGRhdGEuZnJhbWUodHlwZSA9IHJlcChOZXRmbGl4JHR5cGUsIHNhcHBseShOZXRmbGl4X2dlbmVyZXMsIGxlbmd0aCkpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0ZWRfaW4gPSB1bmxpc3QoTmV0ZmxpeF9nZW5lcmVzKSkNCmdlbnJlc19saXN0ZWRfaW4kbGlzdGVkX2luIDwtIGFzLmNoYXJhY3Rlcihnc3ViKCIsIiwiIixnZW5yZXNfbGlzdGVkX2luJGxpc3RlZF9pbikpDQoNCmRmX2xpc3QgID0gZ2VucmVzX2xpc3RlZF9pbiAlPiUgDQogIGdyb3VwX2J5KHR5cGUsIGxpc3RlZF9pbikgJT4lIA0KICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JSANCiAgYXJyYW5nZShkZXNjKGNvdW50KSkgJT4lIHRvcF9uKDEwKQ0KDQpOZXRsZml4X2ZpZzcgPSBwbG90X2x5KGRmX2xpc3QsIHggPSB+bGlzdGVkX2luLCB5ID0gfmNvdW50LA0KICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gJ2JhcicsIGNvbG9yID0gfnR5cGUsDQogICAgICAgICAgICAgICAgICAgICAgIGNvbG9ycyA9IGMoIiNiZDM5MzkiLCAiIzM5OWJhMyIpKSAlPiUNCiAgbGF5b3V0KHhheGlzID0gbGlzdChjYXRlZ29yeW9yZGVyID0gImFycmF5IiwgDQogICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcnlhcnJheSA9IGRmX2xpc3QkbGlzdGVkX2luLCANCiAgICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICdHZW5yZScsDQogICAgICAgICAgICAgICAgICAgICAgdGlja2FuZ2xlID0gNDUpLCANCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICdDb3VudCcpLCANCiAgICAgICAgIHRpdGxlID0gIlRvcCBHZW5yZXMgKE1vdmllIHZzLiBUViBTaG93KSIsIG1hcmdpbiA9IGxpc3QodCA9IDU0KSwNCiAgICAgICAgIGxlZ2VuZCA9IGxpc3QoeCA9IDEwMCwgeSA9IDAuNSkpDQpOZXRsZml4X2ZpZzcNCmBgYA0KV2Ugb2JzZXJ2ZSB0aGF0IHRoZSBtb3N0IHBvcHVsYXIgZ2VucmUgaW4gYm90aCBtb3ZpZXMgYW5kIFRWIHNob3dzIGlzIGludGVybmF0aW9uYWwgY29udGVudCwgd2hpY2ggaXMgZm9sbG93ZWQgYnkgZHJhbWFzIGFuZCBjb21lZGllcy4gVGhlc2UgYXJlIHRoZSB0b3AgdGhyZWUgY2F0ZWdvcmllcyBvbiBOZXRmbGl4IHdpdGggdGhlIG1vc3QgY29udGVudCENCg0KDQojIyMgKipIb3cgYXJlIHRoZSBnZW5lcmVzIGNsdXN0ZXJlZCoqDQoNCmBgYHtyfQ0KbGlicmFyeSh0bSkNCiMgYnVpbGRpbmcgY29ycHVzDQpjb3JwdXMgPC0gQ29ycHVzKFZlY3RvclNvdXJjZShOZXRmbGl4JGxpc3RlZF9pbikpDQoNCiMgY3JlYXRlIHRlcm0gZG9jdW1lbnQgbWF0cml4DQp0ZG0gPC0gVGVybURvY3VtZW50TWF0cml4KGNvcnB1cywgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBsaXN0KG1pbldvcmRMZW5ndGg9YygxLEluZikpKQ0KIyBjb252ZXJ0IHRvIG1hdHJpeA0KbSA8LSBhcy5tYXRyaXgodGRtKQ0KDQojIEhpZXJhcmNoaWNhbCB3b3JkIGNsdXN0ZXJpbmcgdXNpbmcgZGVuZHJvZ3JhbQ0KZGlzdGFuY2UgPC0gZGlzdChzY2FsZShtKSkNCmhjIDwtIGhjbHVzdChkaXN0YW5jZSwgbWV0aG9kID0gIndhcmQuRCIpDQojZnZpel9kZW5kKGhjLCBjZXggPSAwLjUsIGsgPSA0LCBjb2xvcl9sYWJlbHNfYnlfayA9IFRSVUUpDQojZnZpel9kZW5kKGhjLCBjZXggPSAwLjcsIGx3ZCA9IDAuNSwgayA9IDUscmVjdCA9IFRSVUUscmVjdF9maWxsID0gVFJVRSx0eXBlID0gImNpcmN1bGFyIix5bGFiID0iIikNCiNmdml6X2RlbmQoaGMpDQojIENpcmN1bGFyDQpsaWJyYXJ5KGRlbmRleHRlbmQpDQojaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQ0KbGlicmFyeShkcGx5cikNCnJlcXVpcmUoZmFjdG9leHRyYSkNCkNpcmMgPSBmdml6X2RlbmQoaGMsIGNleCA9IDAuNywgbHdkID0gMC41LCBrID0gNSwNCiAgICAgICAgICAgICAgICAgcmVjdCA9IFRSVUUsDQogICAgICAgICAgICAgICAgIGtfY29sb3JzID0gYygiIzQ0MDE1NCIsICIjM2I1MjhiIiwgIiMyMTkxOGMiLCAiIzVlYzk2MiIsICIjZmRlNzI1IiksDQogICAgICAgICAgICAgICAgIHJlY3RfYm9yZGVyID0gYygiIzQ0MDE1NCIsICIjM2I1MjhiIiwgIiMyMTkxOGMiLCAiIzVlYzk2MiIsICIjZmRlNzI1IiksDQogICAgICAgICAgICAgICAgIHJlY3RfZmlsbCA9IFRSVUUsDQogICAgICAgICAgICAgICAgIHR5cGUgPSAiY2lyY3VsYXIiLA0KICAgICAgICAgICAgICAgICB5bGFiID0gIiIpDQpDaXJjDQpgYGANClRoZSBjbHVzdGVycyBvZiBnZW5yZXMgYXJlIGRlcGljdGVkIGluIHRoZSBpbWFnZSBiZWxvdy4gVGhlIGNsdXN0ZXJzIHNob3cgdGhhdCBmYW1pbHkgYW5kIGtpZC1mcmllbmRseSBtb3ZpZXMgYXJlIHBvcHVsYXIuIE1vcmVvdmVyLCB0aGUgbGFyZ2VzdCBnZW5yZSBjbHVzdGVyIGluY2x1ZGVzIHNldmVyYWwgb3RoZXIgZ2VucmVzIGFzIHdlbGwgYXMgdGhyaWxsZXJzLCBjcmltZXMsIGhvcnJvciwgYW5kIHJlYWxpdHkuDQoNCiMjIyAqKk1vdmllIER1cmF0aW9uIGluIFRvcCAxMiBDb3VudHJpZXMqKg0KDQpgYGB7cn0NCm1vdmllX2R1cmF0aW9uID0gbmEub21pdChOZXRmbGl4W05ldGZsaXgkdHlwZSA9PSAiTW92aWUiLF1bLGMoImNvdW50cnkiLCAiZHVyYXRpb24iKV0pDQpEdXJhdGlvbjwtIHN0cnNwbGl0KG1vdmllX2R1cmF0aW9uJGNvdW50cnksIHNwbGl0ID0gIiwgIikNCmR1cmF0aW9uX2Z1bGwgPC0gZGF0YS5mcmFtZShkdXJhdGlvbiA9IHJlcChtb3ZpZV9kdXJhdGlvbiRkdXJhdGlvbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYXBwbHkoRHVyYXRpb24sIGxlbmd0aCkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvdW50cnkgPSB1bmxpc3QoRHVyYXRpb24pKQ0KZHVyYXRpb25fZnVsbCRkdXJhdGlvbiA8LSBhcy5udW1lcmljKGdzdWIoIiBtaW4iLCIiLCBkdXJhdGlvbl9mdWxsJGR1cmF0aW9uKSkNCg0KZHVyYXRpb25fZnVsbF9zdWJzZXQgPC0gZHVyYXRpb25fZnVsbFtkdXJhdGlvbl9mdWxsJGNvdW50cnkgJWluJSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJVbml0ZWQgU3RhdGVzIiwgIkluZGlhIiwgIlVuaXRlZCBLaW5nZG9tIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDYW5hZGEiLCAiRnJhbmNlIiwgIkphcGFuIiwgIlNwYWluIiwgIlNvdXRoIEtvcmVhIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNZXhpY28iLCAiQXVzdHJhbGlhIiwgIkNoaW5hIiwgIlRhaXdhbiIpLF0NCg0KTmV0ZmxpeF9maWc4ID0gcGxvdF9seShkdXJhdGlvbl9mdWxsX3N1YnNldCwgeSA9IH5kdXJhdGlvbiwgY29sb3IgPSB+Y291bnRyeSwgdHlwZSA9ICJib3giKSAlPiUNCiAgbGF5b3V0KHhheGlzID0gbGlzdCh0aXRsZSA9ICJDb3VudHJ5IiksIA0KICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gJ0R1cmF0aW9uIChpbiBtaW4pJyksDQogICAgICAgICB0aXRsZSA9ICJCb3gtUGxvdHMgb2YgTW92aWUgRHVyYXRpb24gaW4gVG9wIDEyIENvdW50cmllcyIsIG1hcmdpbiA9IGxpc3QodCA9IDU0KSwNCiAgICAgICAgIGxlZ2VuZCA9IGxpc3QoeCA9IDEwMCwgeSA9IDAuNSkpDQpOZXRmbGl4X2ZpZzgNCmBgYA0KDQoNCg0KDQojIyMgKipDb25jbHVzaW9uKioNCg0KSXQgaXMgY2xlYXIgdGhhdCBtb3ZpZXMgcGxheSBhIGJpZ2dlciByb2xlIGluIE5ldGZsaXggY29udGVudCBiYXNlZCBvbiB0aGUgdmlzdWFsaXphdGlvbiBhbmQgdGV4dCBhbmFseXNpcyBvZiB0aGUgTmV0ZmxpeCBkYXRhLiBBZGRpdGlvbmFsbHksIHRoZSBkYXRhIHN1bW1hcnkgYW5kIHZpc3VhbGl6YXRpb24gc2hvd2VkIHRoYXQgdGhlIFVuaXRlZCBTdGF0ZXMgd2FzIHRoZSBzb2xlIGNvdW50cnkgYXQgdGhlIHRpbWUgdGhhdCBOZXRmbGl4IGJlZ2FuIGRvbmF0aW5nIGl0cyBjb250ZW50LCB3aGljaCB3YXMgaW4gMjAwOC4gQWRkaXRpb25hbGx5LCBOZXRmbGl4IFRWIHNlcmllcyBhbmQgbW92aWVzIGFyZSByYXRlZCwgYW5kIHRoZXNlIHJhdGluZ3MgYXJlIGNvbnNpc3RlbnQgZm9yIHZhcmlvdXMgYXVkaWVuY2VzLiBUaGUgZXhhbWluYXRpb24gb2YgcnVudGltZSBhbmQgcmFudGluZyBjYXRlZ29yaWVzIHJldmVhbHMgdGhhdCBtb3ZpZXMgYWltZWQgdG93YXJkcyB5b3VuZ3N0ZXJzIGFyZSB0eXBpY2FsbHkgc2hvcnRlci4NCg0KDQo=